/*
* INSANE - Interactive Structural Analysis Environment
*
* Copyright (C) 2003-2005
* Universidade Federal de Minas Gerais
* Escola de Engenharia
* Departamento de Engenharia de Estruturas
* 
* Author's email :    insane@dees.ufmg.br
* Author's website :  http://www.dees.ufmg.br/insane
* 
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
* 
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
package br.ufmg.dees.insane.prep;

import java.util.List;

import br.ufmg.dees.insane.model.geo.GCurve;
import br.ufmg.dees.insane.model.geo.GPoint;
import br.ufmg.dees.insane.model.geo.GRegion;

public class LoftingToLagranT extends Lofting{
	
	private boolean linearDivisor;
	
//*****************************************************************************
	
	public LoftingToLagranT(int ln, int le, int nppe, int ne, boolean ld, GRegion rg){
		this.lastNode = ln;
		this.lastElement = le;
		this.nPointsPerElement = nppe;
		
		int aux1 = 0;
		int aux2 = nPointsPerElement;
		while ( aux2 > 0 ) {
			aux1++;
			aux2 = aux2 - aux1;
		}
		this.transverseCurvePoints = (aux1-1)*ne + 1;
		
		this.linearDivisor = ld;
		this.region = rg;
		
		this.mapNodes();
		this.conectivite();
	}
	
//*****************************************************************************
	
	private void mapNodes(){
		int nPartsBetweenCurves = this.transverseCurvePoints - 1;
		
		int aux1 = 0;
		int aux2 = nPointsPerElement;
		while ( aux2 > 0 ) {
			aux1++;
			aux2 = aux2 - aux1;
		}
		
		List curves = this.region.getCurves();
		GPoint[] curve1 = ((GCurve)curves.get(0)).getDiscrete();
		GPoint[] curve2 = ((GCurve)curves.get(1)).getDiscrete();
		
		int nPoints1 = curve1.length;
		
		double x, y;
		for (int i = 0; i <= nPartsBetweenCurves; i++) {
			double v = (double)i/(nPartsBetweenCurves);
			for (int j = 0; j < nPoints1; j++) {
				lastNode++;
				x = (1.0-v)*curve1[j].getX()+v*curve2[j].getX();
				y = (1.0-v)*curve1[j].getY()+v*curve2[j].getY();
				MapNode mn = new MapNode(x, y, lastNode);
				if ( i==0 || j==0 || i==nPartsBetweenCurves || j==(nPoints1-1) ) {
					mn.setBoundary( true );
				}
				this.nodes.add(mn);
			}
		}
	}
	
//*****************************************************************************
	
	private void conectivite(){
		List curves = this.region.getCurves();
		GPoint[] curve1 = ((GCurve)curves.get(0)).getDiscrete();
		int nPoints1 = curve1.length;
		
		int aux1 = 0;
		int aux2 = nPointsPerElement;
		while ( aux2 > 0 ) {
			aux1++;
			aux2 = aux2 - aux1;
		}
		int nPointsElementSide = aux1;
		
		double xx, yy;
		double xx1 = 0;
		double yy1 = 0;
		double xx2 = 0;
		double yy2 = 0;
		int nElements1 = (nPoints1 - 1)/(nPointsElementSide - 1);
		int nElements2 = (transverseCurvePoints - 1)/(nPointsElementSide - 1);
		int element = this.lastElement;
		MapNode[] corner = new MapNode[4];
		MapNode[] conec = new MapNode[(int)(nPointsElementSide*((nPointsElementSide+1.0)/2))];
		MapNode[] bound = new MapNode[3*(nPointsElementSide-1)];
		for (int i = 0; i < nElements2; i++) {
			for (int j = 0; j < nElements1; j++) {
				int no;
				corner[0] = (MapNode)nodes.get( (nPoints1)*(nPointsElementSide-1)*i+(nPointsElementSide-1)*j+1 - 1 );
				corner[1] = (MapNode)nodes.get( (nPoints1)*(nPointsElementSide-1)*i+(nPointsElementSide-1)*j+nPointsElementSide - 1 );
				corner[2] = (MapNode)nodes.get( (nPoints1)*(nPointsElementSide-1)*(i+1)+(nPointsElementSide-1)*j+nPointsElementSide - 1 );
				corner[3] = (MapNode)nodes.get( (nPoints1)*(nPointsElementSide-1)*(i+1)+(nPointsElementSide-1)*j+1 - 1 );
				double distance1 = corner[0].distance(corner[2]);
				double distance2 = corner[1].distance(corner[3]);
				//	algoritimo para gerar o contorno do elemento no sentido anti-horario
				if ( (distance1/distance2) < 1.01 ) {
					
					element++;
					no = -1;
					// lado inferior
					for (int k = 1; k <= nPointsElementSide; k++) {
						no++;
						bound[no] = (MapNode)nodes.get( (nPoints1)*(nPointsElementSide-1)*(i+1)+(nPointsElementSide-1)*j+k - 1 );
					}
					xx1 = bound[no].getX();
					yy1 = bound[no].getY();
					// diagonal "\"
					for (int k = (nPointsElementSide-2); k >= 0; k--) {
						no++;
						bound[no] = (MapNode)nodes.get( (nPoints1)*(nPointsElementSide-1)*i+(nPointsElementSide-1)*j+1+(1+nPoints1)*k - 1 );
					}
					// lado esquerdo
					for (int k = 1; k <= (nPointsElementSide-2); k++) {
						no++;
						bound[no] = (MapNode)nodes.get( (1+(nPointsElementSide-1)*i)*(nPoints1)+(nPointsElementSide-1)*j+(nPoints1)*(k-1)+1 - 1 );
					}
					
					//	algoritimo para gerar a conectividade do elemento
					no = -1;
					for (int m = 1; m <= nPointsElementSide; m++) {
						for (int n = 1; n <= m; n++) {
							no++;
							conec[no] = (MapNode)nodes.get( i*(nPointsElementSide-1)*nPoints1+j*(nPointsElementSide-1)+(m-1)*nPoints1+(n-1)+1 - 1 );
						}
					}
					MapElement me1 = new MapElement(element, conec, bound);
					me1.setType('T','L');
					this.elements.add(me1);
					
					element++;
					no = -1;
					// lado superior
					for (int k = nPointsElementSide; k > 0; k--) {
						no++;
						bound[no] = (MapNode)nodes.get( (nPoints1)*(nPointsElementSide-1)*i+(nPointsElementSide-1)*j+k - 1 );
					}
					xx2 = bound[no].getX();
					yy2 = bound[no].getY();
					// diagonal "\"
					for (int k = 1; k <= (nPointsElementSide-1); k++) {
						no++;
						bound[no] = (MapNode)nodes.get( (nPoints1)*(nPointsElementSide-1)*i+(nPointsElementSide-1)*j+1+(1+nPoints1)*k - 1 );
						
						if (linearDivisor) {
							double vv = (double)(k)/(nPointsElementSide-1);
							xx = (1.0-vv)*xx2+vv*xx1;
							yy = (1.0-vv)*yy2+vv*yy1;
							bound[no].setLocation(xx,yy);
						}
					}
					// lado direito
					for (int k = (nPointsElementSide-2); k >= 1; k--) {
						no++;
						bound[no] = (MapNode)nodes.get( (1+(nPointsElementSide-1)*i)*(nPoints1)+(nPointsElementSide-1)*(j+1)+(nPoints1)*(k-1)+1 - 1 );
					}
					
					//	algoritimo para gerar a conectividade do elemento
					no = -1;
					for (int m = nPointsElementSide; m >= 1; m--) {
						for (int n = m; n <= nPointsElementSide; n++) {
							no++;
							conec[no] = (MapNode)nodes.get( i*(nPointsElementSide-1)*nPoints1+j*(nPointsElementSide-1)+(m-1)*nPoints1+(n-1)+1 - 1 );
						}
					}
					MapElement me2 = new MapElement(element, conec, bound);
					me2.setType('T','L');
					this.elements.add(me2);
					
				} else {
					
					element++;
					no = -1;
					// lado superior
					for (int k = nPointsElementSide; k > 0; k--) {
						no++;
						bound[no] = (MapNode)nodes.get( (nPoints1)*(nPointsElementSide-1)*i+(nPointsElementSide-1)*j+k - 1 );
						if (k == nPointsElementSide) {
							xx1 = bound[no].getX();
							yy1 = bound[no].getY();
						}
					}
					// lado esquerdo
					for (int k = 1; k <= (nPointsElementSide-2); k++) {
						no++;
						bound[no] = (MapNode)nodes.get( (1+(nPointsElementSide-1)*i)*(nPoints1)+(nPointsElementSide-1)*j+(nPoints1)*(k-1)+1 - 1 );
					}
					// diagonal "/"
					for (int k = (nPointsElementSide-1); k > 0; k--) {
						no++;
						bound[no] = (MapNode)nodes.get( (nPoints1)*(nPointsElementSide-1)*i+(nPointsElementSide-1)*(j+1)+1+(nPoints1-1)*k - 1 );
						
					}
					
					//	algoritimo para gerar a conectividade do elemento
					no = -1;
					for (int m = nPointsElementSide; m >= 1; m--) {
						for (int n = 1; n <= m; n++) {
							no++;
							conec[no] = (MapNode)nodes.get( i*(nPointsElementSide-1)*nPoints1+j*(nPointsElementSide-1)+(nPointsElementSide-m)*nPoints1+(n-1)+1 - 1 );
						}
					}
					MapElement me1 = new MapElement(element, conec, bound);
					me1.setType('T','L');
					this.elements.add(me1);
					
					element++;
					no = -1;
					// lado inferior
					for (int k = 1; k <= nPointsElementSide; k++) {
						no++;
						bound[no] = (MapNode)nodes.get( (nPoints1)*(nPointsElementSide-1)*(i+1)+(nPointsElementSide-1)*j+k - 1 );
						if (k == 1) {
							xx2 = bound[no].getX();
							yy2 = bound[no].getY();
						}
					}
					// lado direito
					for (int k = (nPointsElementSide-2); k >= 1; k--) {
						no++;
						bound[no] = (MapNode)nodes.get( (1+(nPointsElementSide-1)*i)*(nPoints1)+(nPointsElementSide-1)*(j+1)+(nPoints1)*(k-1)+1 - 1 );
					}
					// diagonal "/"
					for (int k = 0; k <= (nPointsElementSide-2); k++) {
						no++;
						bound[no] = (MapNode)nodes.get( (nPoints1)*(nPointsElementSide-1)*i+(nPointsElementSide-1)*j+nPointsElementSide+(nPoints1-1)*k - 1 );
						
						if (linearDivisor && (k > 0) ) {
							double vv = (double)k/(nPointsElementSide-1);
							xx = (1.0-vv)*xx1+vv*xx2;
							yy = (1.0-vv)*yy1+vv*yy2;
							bound[no].setLocation(xx,yy);
						}
					}
					
					//	algoritimo para gerar a conectividade do elemento
					no = -1;
					for (int m = 1; m <= nPointsElementSide; m++) {
						for (int n = 1; n <= m; n++) {
							no++;
							conec[no] = (MapNode)nodes.get( i*(nPointsElementSide-1)*nPoints1+j*(nPointsElementSide-1)+(m-1)*nPoints1+(nPointsElementSide-m)+n - 1 );
						}
					}
					MapElement me2 = new MapElement(element, conec, bound);
					me2.setType('T','L');
					this.elements.add(me2);
				}
			}
		}
	}
		
//*****************************************************************************
}

